package com.zj.inoutput.common;

import cn.hutool.core.util.ZipUtil;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
 * @author DuanYu <a>Email:z-houjie@qq.com</a>
 * @date 2022-02-09 14:47
 */
public class ExportUtil {

    private static final Logger log = LoggerFactory.getLogger(ExportUtil.class);

    private ExportUtil() {
        throw new IllegalStateException("Utility class");
    }

    /**
     * 导出单Sheet的excel数据到HttpResponse
     *
     * @param type      类型
     * @param query     获取数据的方法
     * @param errorData 执行失败的返回数据
     * @param fileName  文件名
     * @param sheetName sheet
     * @param response  response
     * @param <T>       类型
     */
    public static <T> void exportSingleSheetToHttpResponse(Class<T> type, Supplier<List<T>> query, Object errorData, String fileName, String sheetName, HttpServletResponse response) throws IOException {
        try {
            long start = System.currentTimeMillis();
            List<T> list = query.get();
            long end = System.currentTimeMillis();
            log.info("Excel 获取数据耗时：{}ms", end - start);

            setExcelHttpResponse(fileName, response);

            ServletOutputStream outputStream = response.getOutputStream();
            EasyExcelFactory.write(outputStream, type).excelType(ExcelTypeEnum.XLSX).sheet(sheetName).doWrite(list);
        } catch (Exception e) {
            log.error("Excel 导出失败：", e);
            response.reset();
            // 如果生成失败，则转为Json返回
            setJsonHttpResponse(response);

            PrintWriter writer = response.getWriter();
            writer.println(JSON.toJSONString(errorData));
            writer.close();
        }
    }

    /**
     * 设置响应类型为excel
     *
     * @param fileName 文件名称
     * @param response response
     * @throws UnsupportedEncodingException 编码异常
     */
    public static void setExcelHttpResponse(String fileName, HttpServletResponse response) throws UnsupportedEncodingException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
    }

    /**
     * 设置响应类型为excel
     *
     * @param response response
     */
    public static void setJsonHttpResponse(HttpServletResponse response) {
        response.setContentType("application/json");
        response.setCharacterEncoding("utf-8");
    }

    /**
     * 分片
     *
     * @param serialize 压缩后的内容
     * @return 内容片段
     */
    public static List<String> slicing(byte[] serialize) {
        List<String> list = new ArrayList<>();
        // 每片大小（100KB）
        final int slicingSize = 1024 * 100;
        // 初始化偏移量和长度
        int offset = 0, length = slicingSize;
        do {
            if ((serialize.length - offset) <= slicingSize) {
                length = serialize.length - offset;
            }
            list.add(new String(serialize, offset, length, StandardCharsets.ISO_8859_1));
            offset += length;
        } while (offset < serialize.length);
        return list;
    }


    /**
     * 序列化 压缩json
     *
     * @param elements 内容
     * @param <E>      内容类型
     * @return 压缩后内容
     */
    public static <E> byte[] serialize(List<E> elements) {
        String json = JSON.toJSONString(elements);
        byte[] bytes = json.getBytes(StandardCharsets.UTF_8);
        return ZipUtil.gzip(bytes);
    }


    /**
     * 解压
     *
     * @param serialized 压缩内容
     * @param type       转换的类型
     * @param <E>        类型
     * @return 解压的内容
     */
    public static <E> List<E> deserialize(String serialized, Class<E> type) {
        byte[] bytes = serialized.getBytes(StandardCharsets.ISO_8859_1);
        byte[] unzip = ZipUtil.unGzip(bytes);
        return JSON.parseArray(new String(unzip), type);
    }
}
